import { EPSILON } from '@amcax/base';
import { Vector3 } from 'three-legacy';

export function arePointsCollinear(points: Vector3[]): boolean {
  if (points.length < 3) {
    return true;
  }

  const direction = new Vector3().subVectors(points[1], points[0]);

  for (let i = 2; i < points.length; i++) {
    const currentDirection = new Vector3().subVectors(points[i], points[0]);
    const crossProduct = new Vector3().crossVectors(
      direction,
      currentDirection,
    );
    if (crossProduct.length() > EPSILON) {
      return false;
    }
  }
  return true;
}

function findIntersection(
  p1: Vector3,
  dir1: Vector3,
  p2: Vector3,
  dir2: Vector3,
): Vector3 | null {
  const v = new Vector3().subVectors(p2, p1);

  const cross = dir1.clone().cross(dir2);
  if (cross.lengthSq() < EPSILON) {
    return null;
  }

  const t = v.clone().cross(dir2).dot(cross) / cross.lengthSq();

  return p1.clone().add(dir1.multiplyScalar(t));
}

export function arePointsOnCircle(points: Vector3[]): boolean {
  if (points.length < 3) {
    return false;
  }

  const [p1, p2, p3] = points;

  if (arePointsCollinear([p1, p2, p3])) {
    return false;
  }

  const normal = new Vector3()
    .crossVectors(
      new Vector3().subVectors(p2, p1),
      new Vector3().subVectors(p3, p1),
    )
    .normalize();

  const mid1 = new Vector3().addVectors(p1, p2).multiplyScalar(0.5);
  const mid2 = new Vector3().addVectors(p2, p3).multiplyScalar(0.5);

  const dir1 = new Vector3().subVectors(p2, p1).cross(normal).normalize();
  const dir2 = new Vector3().subVectors(p3, p2).cross(normal).normalize();

  const center = findIntersection(mid1, dir1, mid2, dir2);
  if (!center) {
    return false;
  }

  const radius = center.distanceTo(p1);

  return points.every(
    (point) => Math.abs(center.distanceTo(point) - radius) < EPSILON,
  );
}

export function arePointsCoplanar(points: Vector3[]): boolean {
  if (points.length < 4) {
    return true;
  }

  const normal = new Vector3().crossVectors(
    new Vector3().subVectors(points[1], points[0]),
    new Vector3().subVectors(points[2], points[0]),
  );

  for (let i = 3; i < points.length; i++) {
    const vector = new Vector3().subVectors(points[i], points[0]);
    if (Math.abs(normal.dot(vector)) > EPSILON) {
      return false;
    }
  }
  return true;
}
